#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/timer.h>

#define DEV_KEY_NAME			"drv_key"	/* 名字 */
#define CLASS_NAME				"dwk_key_class"

static struct gpio_key{
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
	struct timer_list key_timer;
};

struct fasync_struct *button_fasync;
static struct gpio_key *gpio_keys;

/*环形缓冲区*/
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}
/*环形缓冲区END*/

static void key_timer_expire(unsigned long data)
{
	struct gpio_key *gpio_key = data;
	int val;
	val = gpiod_get_value(gpio_key->gpiod);
	int key;
	
	key  = (gpio_key->gpio << 8) | val;
    put_key(key);
	
	kill_fasync(&button_fasync, SIGIO, POLLIN);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	mod_timer(&gpio_key->key_timer, jiffies + HZ*10);
	return IRQ_HANDLED;
}

static ssize_t gpio_key_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	
	int err;
	int key;
	
	key = get_key();
	err = copy_to_user(buf, &key, 4);
	
	return 4;
}

/*
static unsigned int gpio_key_drv_poll(struct file * pfile, struct poll_table_struct * poll)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(pfile, &gpio_key_wait, poll);
	
    return is_key_buf_empty()? 0 : POLLIN | POLLRDNORM ;
}
*/
static int gpio_key_drv_fsync (int fd, struct file * file, int on)
{
	if (fasync_helper(fd, file, on, &button_fasync) >= 0)
		return 0;
	else
		return -EIO;
	
}

static struct file_operations key_opr = {
    .owner = THIS_MODULE,
    .read = gpio_key_drv_read,
    //.poll = gpio_key_drv_poll,
    .fasync = gpio_key_drv_fsync,
};

static struct ST_KEY_DEV{
	dev_t devid;				/*设备号*/
	struct cdev cdev;
	struct class *class;		/*所在类*/
	struct device *device;		/* 设备 */
	int major;					/*主设备号*/
	int minor;					/*次设备号*/
	struct device_node *node;	/*设备节点*/
}st_key_dev;

static int drv_probe(struct platform_device * pdev)
{
    int err;
    struct device_node *dev = pdev->dev.of_node;
    int count;
    int i;
    enum of_gpio_flags flag;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    count = of_gpio_count(dev);
    if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}else{
        printk("%s %s line %d, there is %d gpio available\n", __FILE__, __FUNCTION__, __LINE__,count);
    }

    gpio_keys = kzalloc(sizeof(struct gpio_key) * count , GFP_KERNEL);

    for(i =0;i<count;i++)
    {
        gpio_keys[i].gpio = of_get_gpio_flags(dev,i,&flag);
        if (gpio_keys[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}

        gpio_keys[i].gpiod = gpio_to_desc(gpio_keys[i].gpio);
        gpio_keys[i].flag = flag & OF_GPIO_ACTIVE_LOW;
        gpio_keys[i].irq = gpiod_to_irq(gpio_keys[i].gpiod);

		/*创建定时器*/
		setup_timer(&gpio_keys[i].key_timer, key_timer_expire, &gpio_keys[i]);
		gpio_keys[i].key_timer.expires = ~0;
		add_timer(&gpio_keys[i].key_timer);
		
		if (flag & OF_GPIO_ACTIVE_LOW)
			flag |= GPIOF_ACTIVE_LOW;       

    }
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    for(i=0;i<count;i++)
    {
        err = request_irq(gpio_keys[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "dwk_key_drv", &gpio_keys[i]);
    }
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    /*create dev node*/
    /*创建设备节点*/
    if(!st_key_dev.major)
    {
        alloc_chrdev_region(&st_key_dev.devid,0,1,DEV_KEY_NAME);
        st_key_dev.major = MAJOR(st_key_dev.devid);
        st_key_dev.minor = MINOR(st_key_dev.devid);
        printk("key major = %d,minor = %d\r\n",st_key_dev.major,st_key_dev.minor);
    }
    
    st_key_dev.cdev.owner = THIS_MODULE;
	cdev_init(&st_key_dev.cdev,&key_opr);

	cdev_add(&st_key_dev.cdev,st_key_dev.devid,1);
    /*创建设备所在类*/
	st_key_dev.class = class_create(THIS_MODULE, CLASS_NAME);
	if(IS_ERR(st_key_dev.class)) {
		cdev_del(&st_key_dev.cdev);
    	unregister_chrdev_region(st_key_dev.devid, 1); /* 注销设备号 */
		return PTR_ERR(st_key_dev.class);
		
	}else{
		printk("create class ok,class name = %s\r\n",CLASS_NAME);
	}

    /*创建设备*/
	device_create(st_key_dev.class, NULL, MKDEV(st_key_dev.major, st_key_dev.minor), NULL, "dwk_dev_key%d", 0); 

    return 0;
}

static int drv_remove(struct platform_device * pdev)
{
    struct device_node *node = pdev->dev.of_node;
    int count;
    int i;
    /*先删除设备，再删除类，最后注销设备号*/
    
	count = of_gpio_count(node);
	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys[i].irq, &gpio_keys[i]);
        del_timer(&gpio_keys[i].key_timer);
	}
    device_destroy(st_key_dev.class, MKDEV(st_key_dev.major, st_key_dev.minor));
    cdev_del(&st_key_dev.cdev);
    unregister_chrdev_region(st_key_dev.devid, 1); /* 注销设备号 */
    class_destroy(st_key_dev.class);

	kfree(gpio_keys);
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    return 0;
}


static const struct of_device_id st_match[] = {
    {.compatible = "dwk,key_drv"},
    {},
};

static const struct platform_driver platform_key = {
    .probe = drv_probe,
    .remove = drv_remove,
    .driver = {
        .name = "dwk_key_drv",
        .of_match_table = st_match,
    },
};

static int __init drv_key_init(void)
{
    printk("key drv init!\r\n");
    /*注册驱动*/
	platform_driver_register(&platform_key);
    return 0;
    
}

static void __exit drv_key_exit(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&platform_key);
}
module_init(drv_key_init);
module_exit(drv_key_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongwenke");